﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security;
using System.Net;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Diagnostics;
using System.Runtime.Serialization;

#if NetFx40
using System.Diagnostics.Contracts;
#endif

#if !Portable
using System.IO.Compression;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
#endif

namespace XYZShell.Helper
{
    /// <summary>
    /// 压缩类型
    /// </summary>
    public enum CompressionType
    {
        /// <summary>
        /// Gzip算法
        /// </summary>
        Gzip,
        /// <summary>
        /// Deflate算法
        /// </summary>
        Deflate
    }
    /// <summary>
    /// 数据辅助
    /// </summary>
    public static class DataHelper
    {

        /// <summary>
        /// 转换为16进制字符串
        /// </summary>
        /// <param name="bs"></param>
        /// <param name="isLowcase"></param>
        /// <returns></returns>
        public static string ToHexString(this IEnumerable<byte> bs, bool isLowcase = false)
        {
            string rtn = "";
            foreach (var item in bs)
            {
                string fmtstr = isLowcase ? "x2" : "X2";
                rtn += item.ToString(fmtstr);
            }
            return rtn;
        }


        /// <summary>
        /// 转换为base64编码
        /// </summary>
        /// <param name="bs"></param>
        /// <returns></returns>
        public static string EncodeBase64(this IEnumerable<byte> bs)
        {
            return Convert.ToBase64String(bs.ToArray());
        }


        /// <summary>
        /// byte数组转换为值类型数组
        /// </summary>
        /// <typeparam name="T">值类型</typeparam>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static T[] BytesToArray<T>( this IEnumerable<byte> bytes )
        {
            MemoryStream ms=new MemoryStream( bytes.ToArray() );
            BinaryReader br=new BinaryReader( ms );

            List<T> rtn=new List<T>();
            TypeCode typeCode=Type.GetTypeCode( typeof( T ) );
            switch (typeCode)
            {
                case TypeCode.Single:
                    {
                        for (int i=0; i < ms.Length / sizeof( float ); i++)
                        {
                            rtn.Add( (T)(object)br.ReadSingle() );
                        }
                    }
                    break;

                case TypeCode.Double:
                    {

                        for (int i=0; i < ms.Length / sizeof( double ); i++)
                        {
                            rtn.Add( (T)(object)br.ReadDouble() );
                        }
                    }
                    break;
                case TypeCode.Int32:
                    {
                        for (int i=0; i < ms.Length / sizeof( int ); i++)
                        {
                            rtn.Add( (T)(object)br.ReadInt32() );
                        }
                    }
                    break;
            }

            return rtn.ToArray();

        }

        /// <summary>
        /// 字节数组转换为值类型对象数组
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="T">对象类型</param>
        /// <returns></returns>
        public static object[] BytesToArray( IEnumerable<byte> bytes, Type T )
        {
            MemoryStream ms=new MemoryStream( bytes.ToArray() );
            BinaryReader br=new BinaryReader( ms );

            List<object > rtn=new List<object>();
            TypeCode typeCode=Type.GetTypeCode( T );
            switch (typeCode)
            {
                case TypeCode.Single:
                    {
                        for (int i = 0; i < ms.Length / sizeof( float ); i++)
                        {
                            rtn.Add( br.ReadSingle() );
                        }
                    }
                    break;

                case TypeCode.Double:
                    {

                        for (int i = 0; i < ms.Length / sizeof( double ); i++)
                        {
                            rtn.Add( br.ReadDouble() );
                        }
                    }
                    break;
                case TypeCode.Int32:
                    {
                        for (int i = 0; i < ms.Length / sizeof( int ); i++)
                        {
                            rtn.Add( br.ReadInt32() );
                        }
                    }
                    break;
                default:
                    {
                        throw new NotSupportedException();
                    };
            }

            return rtn.ToArray();

        }

		/// <summary>
		/// 获取值类型字节
		/// </summary>
		/// <param name="o"></param>
		/// <param name="IsLittleEndian"></param>
		/// <returns></returns>
		private static byte[] GetBytesCommonValType( this ValueType o, bool IsLittleEndian = true )
        {

            byte[] rtn = null;
            if (o is float)
            {
                rtn = BitConverter.GetBytes( (float)o );
            }

            else if (o is double)
            {
                rtn = BitConverter.GetBytes( (double)o );
            }
            else if (o is short)
            {
                rtn = BitConverter.GetBytes( (short)o );
            }
            else if (o is ushort)
            {
                rtn = BitConverter.GetBytes( (ushort)o );
            }
            else if (o is int)
            {
                rtn = BitConverter.GetBytes( (int)o );
            }
            else if (o is uint)
            {
                rtn = BitConverter.GetBytes( (uint)o );
            }
            else if (o is long)
            {
                rtn = BitConverter.GetBytes( (long)o );
            }
            else if (o is ulong)
            {
                rtn = BitConverter.GetBytes( (ulong)o );
            }
            else
            {
                throw new NotSupportedException();
            }
            if (BitConverter.IsLittleEndian != IsLittleEndian)
            {
                rtn = rtn.Reverse().ToArray();
            }
            return rtn;
        }

        /// <summary>
        /// 通过Marshal类获取结构体字节
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="val"></param>
        /// <param name="IsLittleEndian">是否是小端字节</param>
        /// <returns></returns>
        public static byte[] GetBytes( this ValueType val, bool IsLittleEndian = true ) 
        {
            if (val.IsCommonValueType())
            {
                return GetBytesCommonValType( val,IsLittleEndian );
            }
#if Portable
            throw new NotSupportedException();
#else
            Debug.Assert( (val.GetType().Attributes & TypeAttributes.SequentialLayout )!= 0 );

            int sz = Marshal.SizeOf( val );
            IntPtr ptr = Marshal.AllocHGlobal( sz );
            Marshal.StructureToPtr( val, ptr, false );
            byte[] rtn = new byte[sz];
            Marshal.Copy( ptr, rtn, 0, sz );
            Marshal.FreeHGlobal( ptr );
            if (BitConverter.IsLittleEndian != IsLittleEndian)
            {
                rtn = rtn.Reverse().ToArray();
            }
            return rtn;
#endif
        }

        /// <summary>
        /// Crc16计算
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static ushort CRC16( this IEnumerable<byte> data )
        {
            byte[] bs = data.ToArray();
            int count = bs.Length;
            byte[] buf = new byte[bs.Length + 2];
            bs.CopyTo( buf, 0 );
            int ptr = 0;
            int i = 0;
            ushort crc = 0;
            byte crc1, crc2, crc3;
            crc1 = buf[ptr++];
            crc2 = buf[ptr++];
            buf[count] = 0;
            buf[count + 1] = 0;
            while (--count >= 0)
            {
                crc3 = buf[ptr++];
                for (i = 0; i < 8; i++)
                {
                    if (( ( crc1 & 0x80 ) >> 7 ) == 1)//判断crc1高位是否为1 
                    {
                        crc1 = (byte)( crc1 << 1 ); //移出高位 
                        if (( ( crc2 & 0x80 ) >> 7 ) == 1)//判断crc2高位是否为1 
                        {
                            crc1 = (byte)( crc1 | 0x01 );//crc1低位由0变1 
                        }
                        crc2 = (byte)( crc2 << 1 );//crc2移出高位 
                        if (( ( crc3 & 0x80 ) >> 7 ) == 1) //判断crc3高位是否为1 
                        {
                            crc2 = (byte)( crc2 | 0x01 ); //crc2低位由0变1 
                        }
                        crc3 = (byte)( crc3 << 1 );//crc3移出高位 
                        crc1 = (byte)( crc1 ^ 0x10 );
                        crc2 = (byte)( crc2 ^ 0x21 );
                    }
                    else
                    {
                        crc1 = (byte)( crc1 << 1 ); //移出高位 
                        if (( ( crc2 & 0x80 ) >> 7 ) == 1)//判断crc2高位是否为1 
                        {
                            crc1 = (byte)( crc1 | 0x01 );//crc1低位由0变1 
                        }
                        crc2 = (byte)( crc2 << 1 );//crc2移出高位 
                        if (( ( crc3 & 0x80 ) >> 7 ) == 1) //判断crc3高位是否为1 
                        {
                            crc2 = (byte)( crc2 | 0x01 ); //crc2低位由0变1 
                        }
                        crc3 = (byte)( crc3 << 1 );//crc3移出高位 
                    }
                }
            }
            crc = (ushort)( ( crc1 << 8 ) + crc2 );
            return crc;
        }


		/// <summary>
		/// Crc32计算
		/// </summary>
		/// <param name="data"></param>
		/// <seealso cref="DamienG.Security.Cryptography.Crc32"/>
		/// <remarks>Copyright (c) Damien Guard.  All rights reserved</remarks>
		/// <returns></returns>
		public static uint CRC32( this IEnumerable<byte> data )
		{
			return DamienG.Security.Cryptography.Crc32.Compute( data.ToArray() );
		}



#if !Portable
        /// <summary>
        /// 通过Gzip或Deflate算法解压缩
        /// </summary>
        /// <param name="bs"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static IEnumerable<byte> DeCompression(this IEnumerable<byte> bs, CompressionType type = CompressionType.Gzip)
        {
            switch (type)
            {
                case CompressionType.Gzip:
                    {
                        GZipStream stream = new GZipStream(new MemoryStream(bs.ToArray()), CompressionMode.Decompress);

                        int b = 0;

                        while ((b = stream.ReadByte()) != -1)
                        {
                            yield return (byte)b;
                        }
                    }
                    break;
                case CompressionType.Deflate:
                    {
                        DeflateStream stream = new DeflateStream(new MemoryStream(bs.ToArray()), CompressionMode.Decompress);

                        int b = 0;

                        while ((b = stream.ReadByte()) != -1)
                        {
                            yield return (byte)b;
                        }
                    }
                    break;
            }
        }
        /// <summary>
        /// 通过指定编码转换为字符串
        /// </summary>
        /// <param name="bs"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        [Obsolete("该函数已过时,用GetString代替")]
        public static string ToString(this IEnumerable<byte> bs, Encoding encoding)
        {
            return encoding.GetString(bs.ToArray());
        }

        /// <summary>
        /// 通过指定编码转换为字符串
        /// </summary>
        /// <param name="bs"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public static string GetString(this IEnumerable<byte> bs, Encoding encoding)
        {
            return encoding.GetString(bs.ToArray());
        }

        /// <summary>
        /// 通过指定编码转换为字符串
        /// </summary>
        /// <param name="bs"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public static string GetString(this IEnumerable<byte> bs, string encodingName)
        {
            Encoding encoding = Encoding.GetEncoding(encodingName);
            return encoding.GetString(bs.ToArray());
        }

        /// <summary>
        /// 通过指定编码转换为字符串
        /// </summary>
        /// <param name="bs"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public static string GetString(this IEnumerable<byte> bs, int codepage)
        {
            Encoding encoding = Encoding.GetEncoding(codepage);
            return encoding.GetString(bs.ToArray());
        }


		/// <summary>
		/// 通过BinaryFormatter反序列化
		/// </summary>
		/// <param name="bs"></param>
		/// <returns></returns>
		public static object DeserialToObject( this  IEnumerable<byte> bs )
        {
            BinaryFormatter bf = new BinaryFormatter();
            MemoryStream ms = new MemoryStream(bs.ToArray());
            return bf.Deserialize( ms);
            
        }

		/// <summary>
		/// 通过BinaryFormatter反序列化
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="bs"></param>
		/// <returns></returns>
		public static T DeserialToObject<T>( this  IEnumerable<byte> bs ,SerializeType serialType=SerializeType.Binary)

        {
            IFormatter bf = new BinaryFormatter();
			if (serialType==SerializeType.Xml)
			{
			
			}
            MemoryStream ms = new MemoryStream( bs.ToArray() );
            return (T)bf.Deserialize( ms );

        }

		/// <summary>
		/// 使用MD5CryptoServiceProvider计算md5值
		/// </summary>
		/// <param name="bs"></param>
		/// <returns></returns>
		public static IEnumerable<byte> Md5( this IEnumerable<byte> bs )
		{
			MD5 md5 = new MD5CryptoServiceProvider ();
			
			return md5.ComputeHash( bs.ToArray() );
		}
		/// <summary>
		/// 使用SHA1CryptoServiceProvider计算的sha1值
		/// </summary>
		/// <param name="bs"></param>
		/// <returns></returns>
		public static IEnumerable<byte> Sha1( this IEnumerable<byte> bs )
		{
			SHA1 sha = new SHA1CryptoServiceProvider();
			//SHA1 sha = new SHA1Cng();
			return sha.ComputeHash( bs.ToArray() );
		}


		/// <summary>
		/// 从字节数组转换为结构体
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="bs"></param>
		/// <seealso cref="DataHelper.GetBytes"/>
		/// <returns></returns>
		public static T GetStruct<T>( this IEnumerable<byte> bs )
		{
			return GetStruct<T>( bs, 0 );
		}

		/// <summary>
		/// 从字节数组转换为结构体
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="bs"></param>
		/// <param name="offset">数组开始偏移的下标</param>
		/// <returns></returns>
		public static T GetStruct<T>( this IEnumerable<byte> bs,int offset )
		{
			int sz = Marshal.SizeOf( typeof( T ) );
			IntPtr ptr = Marshal.AllocHGlobal( sz );
			Marshal.Copy( bs.ToArray(),offset, ptr, sz );
			T rtn = (T)Marshal.PtrToStructure( ptr, typeof( T ) );
			Marshal.FreeHGlobal( ptr );
			return rtn;
		}


#endif
    }

#if !Portable
	/// <summary>
	/// 序列化类型
	/// </summary>
	public enum SerializeType
	{
		Binary,
		Xml
	}
#endif
}
